package com.ronan.imweb.service.impl;

import cn.hutool.core.util.IdUtil;
import com.ronan.common.constant.Constants;
import com.ronan.common.pojo.NettyInfo;
import com.ronan.common.pojo.RimUmsInfo;
import com.ronan.common.response.Result;
import com.ronan.imweb.dao.UmsInfoDao;
import com.ronan.imweb.model.UmsInfo;
import com.ronan.imweb.req.LoginUmsReq;
import com.ronan.imweb.req.RegisterUmsReq;
import com.ronan.imweb.service.RedisService;
import com.ronan.imweb.service.UmsInfoCacheService;
import com.ronan.imweb.service.UmsInfoService;
import com.ronan.imweb.vo.LoginUmsVo;
import com.ronan.imweb.vo.RegisterUmsVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * FileName    : com.ronan.imweb.service.impl
 * Description :
 *
 * @author : ronan
 * @version : 1.0
 * Create Date : 2021/3/16 13:51
 **/
@Service
public class UmsInfoServiceImpl implements UmsInfoService {

    /** 初始值 */
    private static int index = 10;
    @Autowired
    private RedisService redisService;
    @Autowired
    private UmsInfoDao umsInfoDao;
    @Autowired
    private UmsInfoCacheService umsInfoCacheService;

    /**
     * 1、注册im用户
     *
     * @param req 请求参数
     * @return {@link Result < RegisterUmsVo >}
     */
    @Override
    public Result<RegisterUmsVo> register(RegisterUmsReq req) {

        // 默认值
        String accid = IdUtil.simpleUUID();
        String name = IdUtil.fastSimpleUUID();
        String token = IdUtil.randomUUID();


        // step1.判断数据库是否有当前账号
        UmsInfo umsInfo = new UmsInfo();
        umsInfo.setAccid(StringUtils.isEmpty(req.getAccid()) ? accid : req.getAccid());
        umsInfo.setName(StringUtils.isEmpty(req.getName()) ? name : req.getName());
        umsInfo.setIcon(req.getIcon());
        umsInfo.setToken(StringUtils.isEmpty(req.getToken()) ? token : req.getToken());
        umsInfo.setSign(req.getSign());
        umsInfo.setEmail(req.getEmail());
        umsInfo.setBirth(req.getBirth());
        umsInfo.setMobile(req.getMobile());
        umsInfo.setGender(req.getGender());
        umsInfo.setMute(req.getMute());
        umsInfo.setEx(req.getEx());

        UmsInfo res = umsInfoDao.searchAllByParams(umsInfo);
        if (null != res) {
            return Result.failed("当前账号已注册!");
        }

        // step2.注册
        umsInfo.setId(IdUtil.fastSimpleUUID());
        int i = umsInfoDao.insertSelective(umsInfo);
        if (i < 1) {
            return Result.failed("注册失败,请重试!");
        }
        // vo
        RegisterUmsVo vo = new RegisterUmsVo();
        vo.setAccid(umsInfo.getAccid());
        vo.setName(umsInfo.getName());
        vo.setToken(umsInfo.getToken());
        return Result.ok(vo);
    }

    /**
     * 2、登录
     *
     * @param req 请求参数
     * @return {@link Result< LoginUmsVo >}
     */
    @Override
    public Result<LoginUmsVo> login(LoginUmsReq req) {

        // step1.判断账号密码是否正确
        UmsInfo umsInfo = new UmsInfo();
        umsInfo.setAccid(req.getAccid());
        umsInfo.setToken(req.getToken());
        UmsInfo res = umsInfoDao.searchAllByParams(umsInfo);
        if (null == res || !umsInfo.getAccid().equals(res.getAccid()) || !umsInfo.getToken().equals(res.getToken())) {
            return Result.failed("登录失败,账号或者密码错误!");
        }

        // step2.查询redis,判断当前用户是否重复登录.①保存登录信息到redis以及本地. ②保存登录状态到redis以及本地
        // 判断是否重复登录 => 判断redis是否有值
        if (judgeLogin(res)) {
            return Result.failed("重复登录!");
        }
        // 登录信息: key(accid):value(token)
        // 登录状态: set(accid)
        saveLogin(res);

        // step3.返回可连接的长连接服务器ip,port
        NettyInfo netty = poll();
        return Result.ok(new LoginUmsVo(netty.getHost(), netty.getPort()));
    }

    /**
     * 3、离线
     *
     * @param ums 口头语
     */
    @Override
    public void offline(RimUmsInfo ums) {
        removeLogin(ums);
    }

    /**
     * 4、在线用户
     *
     * @return {@link Result<Set<RimUmsInfo>>}
     */
    @Override
    public Result<Set<RimUmsInfo>> onlineUms() {
        return Result.ok(umsInfoCacheService.onlineUms());
    }

    /**
     * 轮询 Netty
     *
     * @return {@link NettyInfo}
     */
    public NettyInfo poll() {
        // redis获取Set<NettyInfo>
        Set<Object> set = redisService.sMembers(Constants.NETTY_INFO_PREFIX);
        // Set -> List
        List<Object> list = new ArrayList<>(set);
        if (list.isEmpty()) {
            return null;
        }
        int x = index;
        x = (x + 1) % list.size();
        index++;
        return (NettyInfo) list.get(x);
    }

    /**
     * 删除登录
     */
    private void removeLogin(RimUmsInfo ums) {
        // 删除登录信息
        redisService.del(Constants.ACCOUNT_PREFIX + ums.getAccid());
        // 删除登录状态
        redisService.sRemove(Constants.LOGIN_STATUS_PREFIX, ums.getAccid());
        // 删除路由 => 用户建立长连接时,会保存一份路由信息,ip:port
        redisService.del(Constants.ROUTE_PREFIX + ums.getAccid());
    }

    /**
     * 保存登录
     *
     * @param res res
     */
    private void saveLogin(UmsInfo res) {
        // 登录信息: key(accid):value(token)
        redisService.set(Constants.ACCOUNT_PREFIX + res.getAccid(), res.getToken());
        // 登录状态: set(accid)
        redisService.sAdd(Constants.LOGIN_STATUS_PREFIX, res.getAccid());
    }

    /**
     * 判断登录
     *
     * @param res res
     * @return boolean
     */
    private boolean judgeLogin(UmsInfo res) {
        // 从redis获取token
        String token = (String) redisService.get(Constants.ACCOUNT_PREFIX + res.getAccid());
        // SisMember 判断key是否是集合的元素
        Boolean isMember = redisService.sIsMember(Constants.LOGIN_STATUS_PREFIX, res.getAccid());
        return null != token && isMember;
    }
}
